# -*- conding:utf-8 -*-
import requests
import execjs
from bs4 import BeautifulSoup
import pandas as pd
import csv
from datetime import datetime
import re
import time
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class School:
    account = ''  # 你的学号
    passwd = ''  # 你的密码
    encoded = '' #  账号密码加密后的东西
    cookies = ''
    def __init__(self):
        self.ses = requests.session()
        # 配置重试策略，缓解偶发的网络/服务端抖动
        retry_strategy = Retry(
            total=3,
            read=3,
            connect=3,
            backoff_factor=0.5,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=frozenset(["HEAD", "GET", "OPTIONS", "POST"])
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.ses.mount("http://", adapter)
        self.ses.mount("https://", adapter)
 
    def get_js(self, msg):  # python 调用JS加密 返回 加密后的结果
        with open('./decrypt.js', encoding='utf-8') as f:
            js = execjs.compile(f.read())
            return js.call('encodeInp', msg)
 
    def get_login_cookies(self):
        header = {
            "Content-Type": "text/html;charset=GBK",
            "Vary": "Accept-Encoding"
        }
        url = "https://jw.wfu.edu.cn/jsxsd/"
        self.ses.get(url=url, headers=header, timeout=1000)
        cookies = self.ses.cookies.get_dict()  # 获得临时的cookies
        jsessionid = cookies.get('JSESSIONID', '')
        self.cookies = f"JSESSIONID={jsessionid}"
        # 不再打印临时cookies
 
 
    def login(self, cookies, jsmsg):
        header = {
            "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;"
                     "q=0.8,application/signed-exchange;v=b3",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Cache-Control": "max-age=0",
            "Content-Length": "47",
            "Content-Type": "application/x-www-form-urlencoded",  # 接收类型
            "Cookie": self.cookies,
            "Host": "jw.wfu.edu.cn",
            "Origin": "https://jw.wfu.edu.cn",
            "Proxy-Connection": "keep-alive",
            "Referer": "https://jw.wfu.edu.cn/jsxsd/",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent":"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 "
                         "(KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36",
        }
        PostData = {
            'encoded': jsmsg  # 账号密码加密后的东西
        }
        url = 'https://jw.wfu.edu.cn/jsxsd/xk/LoginToXk'
        response = self.ses.post(url, headers=header, data=PostData, timeout=1000)  
        # 解析响应HTML
        soup = BeautifulSoup(response.text, 'html.parser')
        title_tag = soup.find('title')
        title = title_tag.text.strip() if title_tag else ''
        
        # 检查标题是否为'入口选择'
        if title != '入口选择':
            # 账号或密码错误
            print("❌ 登录失败: 账号或密码错误!")
            return False
        
        new_cookies = self.ses.cookies.get_dict()
        jsessionid = new_cookies.get('JSESSIONID', '')
        #  cookies 更新
        self.cookies = f"JSESSIONID={jsessionid}"
        # 不再打印登录成功信息
        return True


 
    def login_in(self, account, psw):  # 输入账号密码，让cookies 生效
        jsmsg = str(self.get_js(account)) + "%%%" + str(self.get_js(psw)) # 获得加密后的东西
        self.get_login_cookies()  # cookies 初始化
        login_result = self.login(account, jsmsg)
        if not login_result:
            return False
        # 登录通过后等待门户就绪，避免后续接口因加载未完成而失败
        self.ensure_portal_ready(max_retries=5, delay_sec=0.8)
        return True

    def ensure_portal_ready(self, max_retries=5, delay_sec=0.8):
        """
        登录后有时门户页面尚未完全就绪，导致随后的请求失败。
        通过轮询关键页面，等待系统就绪后再继续。
        """
        check_urls = [
            "https://jw.wfu.edu.cn/jsxsd/framework/xsMain.jsp",
            "https://jw.wfu.edu.cn/jsxsd/framework/xsMain_new.jsp?t1=1"
        ]
        for _ in range(max_retries):
            for url in check_urls:
                try:
                    resp = self.ses.get(url, timeout=10)
                    if resp.status_code == 200:
                        soup = BeautifulSoup(resp.text, 'html.parser')
                        # 页面包含用户顶部信息块时，认为已就绪
                        if soup.find('div', class_='middletopdwxxcont') is not None:
                            return True
                        # 或者标题存在且非登录页，作为弱判断
                        title_tag = soup.find('title')
                        if title_tag and title_tag.text.strip() and title_tag.text.strip() != '登录':
                            return True
                except requests.RequestException:
                    pass
            time.sleep(delay_sec)
        return False

    def get_class_schedule(self, semester="2025-2026-1"):
        # 确保已登录
        if not self.cookies:
            # 不再打印请先登录提示
            return None

        url = "https://jw.wfu.edu.cn/jsxsd/xskb/xskb_list.do"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
            "Referer": "https://jw.wfu.edu.cn/jsxsd/framework/xsMain.jsp",
        }
        data = {
            "xnxq01id": semester,  # 学期参数，如 2025-2026-1
            "sfFD": "1",  # 是否分单双周（1=是）
        }

        # 发送 POST 请求（Session 会自动管理 Cookie）
        response = self.ses.post(url, headers=headers, data=data)
        # print(response.text)
        if response.status_code != 200:
            # 不再打印请求失败信息
            return None

        return response.text  # 返回 HTML 页面
    
    def parse_class_schedule(self, html):
        soup = BeautifulSoup(html, 'html.parser')
        table = soup.find('table', {'id': 'kbtable'})
        if not table:
            # 不再打印未找到课表数据提示
            return None

        schedule = []
        rows = table.find_all('tr')[1:]  # 跳过表头

        for row in rows:
            time_slot = row.find('th').get_text(strip=True)
            days = row.find_all('td')
            
            # 格式化节次信息，使其与process_schedule_for_db方法中的处理逻辑一致
            # 检查是否已经是[xx节]格式
            bracket_match = re.match(r'\[(\d+)[\-\d]+节\]', time_slot)
            if bracket_match:
                # 提取所有数字
                nums = re.findall(r'\d+', time_slot)
                if nums:
                    # 去掉0前缀
                    start = nums[0].lstrip('0')
                    end = nums[-1].lstrip('0')
                    time_slot = f'[{start}-{end}节]'
            else:
                # 处理"1,2"格式
                time_match = re.match(r'(\d+),(\d+)', time_slot)
                if time_match:
                    start, end = time_match.groups()
                    # 去掉0前缀
                    start = start.lstrip('0')
                    end = end.lstrip('0')
                    time_slot = f'[{start}-{end}节]'
            
            for i, day in enumerate(days):
                day_name = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"][i]
                
                # 分别处理kbcontent1和kbcontent
                kbcontent1_divs = day.find_all('div', {'class': 'kbcontent1'})
                kbcontent_divs = day.find_all('div', {'class': 'kbcontent'})
                
                # 优先使用kbcontent（详细信息），如果没有则使用kbcontent1
                target_divs = kbcontent_divs if kbcontent_divs else kbcontent1_divs
                
                for div in target_divs:
                    if not div.text.strip() or div.text.strip() == '&nbsp;':
                        continue
                    
                    # 分割多段课程（用----------------------或---------------------分割）
                    content = div.get_text()
                    course_blocks = content.split('----------------------')
                    if len(course_blocks) == 1:
                        course_blocks = content.split('---------------------')
                    
                    # 查找所有font标签用于教室信息提取
                    all_font_tags = div.find_all('font')
                    
                    # 为每个课程块创建一个副本，以便单独提取信息
                    for block_index, block in enumerate(course_blocks):
                        block = block.strip()
                        if not block:
                            continue
                        
                        # 获取课程名称（第一个非空文本）
                        course_name = None
                        teacher = None
                        weeks = None
                        classroom = None
                        font_teacher = None
                        
                        # 获取课程名称（在第一个font标签之前的文本）
                        text_parts = block.split('\n')
                        if text_parts:
                            course_name = text_parts[0].strip()
                        
                        # 从当前课程块的文本中提取教室信息
                        # 查找课程块文本中的教室号（通常是最后一个非空行，且是纯数字）
                        lines = block.split('\n')
                        for line in reversed(lines):
                            line = line.strip()
                            if line and line.isdigit() and len(line) >= 3:
                                classroom = line
                                break
                        
                        # 如果从课程块文本中没有提取到教室信息，再尝试从font标签中提取
                        # 这里需要为每个课程块单独提取，避免后面课程的信息覆盖前面的
                        if not classroom:
                            # 查找教室信息的font标签（假设是按照顺序对应的）
                            for font in all_font_tags:
                                title = font.get('title', '')
                                text = font.get_text(strip=True)
                                
                                if title == '老师':
                                    font_teacher = text
                                elif title in ['周次(节次)', '周次']:
                                    # 提取周次信息，去掉节次部分
                                    if '(' in text and ')' in text:
                                        weeks = text.split('(')[0].strip()
                                    else:
                                        weeks = text.strip()
                                elif title == '教室':
                                    # 只有当我们还没有设置教室信息时，才使用font标签中的信息
                                    if not classroom:
                                        classroom = text
                        
                        # 即使从font标签中提取到了周次信息，也尝试从课程名称中提取周次信息
                        # 这是为了解决周次信息提取错误的问题
                        if course_name:
                            # 与process_schedule_for_db方法保持一致的周次提取逻辑
                            # 首先尝试匹配带"(周)"后缀的周次信息，如"8(周)"或"1-7(周)"
                            course_week_match = re.search(r'(\d+(?:[-\d,]+)?)\(周\)', course_name)
                            if course_week_match:
                                course_weeks = course_week_match.group(1)
                            else:
                                # 如果没有找到带"(周)"后缀的，尝试匹配可能的周次信息
                                # 优先匹配单独的数字（如"8"）
                                single_week_match = re.search(r'(?<!节)(?<!\d)\d+(?!\d)(?!节)', course_name)
                                if single_week_match:
                                    course_weeks = single_week_match.group(0)
                                else:
                                    # 最后尝试匹配数字范围（如"1-7"），但避免匹配节次信息
                                    range_week_match = re.search(r'(?<!节)(?<!\d)\d+[-\d,]+(?!\d)(?!节)', course_name)
                                    if range_week_match:
                                        course_weeks = range_week_match.group(0)
                            # 如果从课程名称中提取到了周次信息，优先使用它
                            if course_weeks:
                                weeks = course_weeks

                        # 如果没有找到课程名称，尝试从block的文本中提取
                        if not course_name:
                            full_text = block
                            if full_text and full_text != '&nbsp;':
                                # 取第一个换行符之前的内容作为课程名
                                course_name = full_text.split('\n')[0].strip()

                        # 从课程名称中提取节次信息（如[01-02-03-04节]）
                        section_match = re.search(r'\[(\d+(?:-\d+)*)节\]', course_name)
                        if section_match:
                            section_text = section_match.group(1)
                            # 提取所有数字
                            nums = section_text.split('-')
                            if nums:
                                # 去掉0前缀
                                start = nums[0].lstrip('0')
                                end = nums[-1].lstrip('0')
                                # 更新time_slot为正确的节次信息
                                time_slot = f'[{start}-{end}节]'

                        # 增强从课程名称中提取教师信息的逻辑
                        # 优先从课程名称中提取教师信息，而不是依赖font标签
                        
                        # 1. 首先尝试从课程名称中提取教师姓名（在周次信息之前）
                        # 匹配模式：课程名+教师名+周次信息
                        # 例如：国画基础程涛1-4(周) -> 提取"程涛"
                        teacher_from_course = None
                        
                        # 先清理课程名称，移除节次信息
                        course_for_teacher_extraction = re.sub(r'\[(\d+(?:-\d+)*)节\]', '', course_name)
                        
                        # 新的提取逻辑：从课程名称末尾开始，找到周次信息之前的教师姓名
                        # 1. 先找到周次信息的位置
                        week_match = re.search(r'\d+[-\d,]*\(周\)', course_for_teacher_extraction)
                        if week_match:
                            week_start = week_match.start()
                            # 从周次信息之前提取2-3个汉字作为教师姓名
                            before_week = course_for_teacher_extraction[:week_start]
                            
                            # 从末尾开始尝试2-3个汉字
                            for length in range(3, 1, -1):
                                if len(before_week) >= length:
                                    candidate = before_week[-length:]
                                    
                                    # 检查这个候选是否看起来像姓名
                                    # 改进策略：使用常见姓氏列表来判断，而不是过滤课程关键词
                                    # 这样可以避免过度过滤导致的问题
                                    
                                    # 常见姓氏列表（简化版）
                                    common_surnames = {
                                        '王', '李', '张', '刘', '陈', '杨', '黄', '赵', '周', '吴',
                                        '徐', '孙', '马', '朱', '胡', '林', '郭', '何', '高', '罗',
                                        '郑', '梁', '谢', '宋', '唐', '许', '韩', '冯', '邓', '曹',
                                        '彭', '曾', '萧', '田', '董', '袁', '潘', '于', '蒋', '蔡',
                                        '余', '杜', '叶', '程', '魏', '苏', '吕', '丁', '任', '沈',
                                        '姚', '卢', '姜', '崔', '钟', '谭', '陆', '汪', '范', '金',
                                        '石', '廖', '贾', '夏', '韦', '傅', '方', '白', '邹', '孟',
                                        '熊', '秦', '邱', '江', '尹', '薛', '闫', '段', '雷', '侯',
                                        '龙', '史', '陶', '黎', '贺', '顾', '毛', '郝', '龚', '邵',
                                        '吉', '安', '万', '康', '常', '贺', '苗', '晁', '甘', '尚'
                                    }
                                    
                                    # 课程关键词列表（避免将课程词汇误识别为姓名）
                                    course_end_keywords = {'史', '学', '论', '法', '术', '概', '读', '化'}
                                    
                                    # 检查候选是否只包含中文字符，且第一个字符是常见姓氏
                                    # 同时要避免课程关键词的干扰
                                    if (re.match(r'^[\u4e00-\u9fa5]+$', candidate) and 
                                        len(candidate) >= 2 and 
                                        candidate[0] in common_surnames and
                                        not (len(candidate) == 3 and candidate[0] in course_end_keywords)):
                                        teacher_from_course = candidate
                                        break
                        
                        # 2. 如果从课程名称中提取到了教师信息，优先使用
                        if teacher_from_course:
                            teacher = teacher_from_course
                        # 3. 如果没有从课程名称中提取到，再检查是否有明确的教师称呼（老师、教授等）
                        elif not teacher:
                            # 优先匹配单个汉字+称呼的模式
                            teacher_title_match = re.search(r'([\u4e00-\u9fa5])(老师|教授|讲师|助教)\s*', course_name)
                            if not teacher_title_match:
                                # 如果没找到单个汉字+称呼，尝试匹配多个汉字+称呼
                                teacher_title_match = re.search(r'(.*?)(老师|教授|讲师|助教)\s*', course_name)
                            if teacher_title_match:
                                teacher = teacher_title_match.group(1).strip()
                                # 对于带称呼的情况，通常只需要姓氏
                                if len(teacher) > 1:
                                    # 尝试提取姓氏（假设姓氏是第一个字符）
                                    teacher = teacher[0]
                                course_name = course_name.replace(teacher_title_match.group(0), '').strip()
                        # 4. 如果以上都没有，使用百家姓匹配课程名称中的完整姓名和姓氏
                        elif not teacher:
                            # 这个逻辑已经被上面的新逻辑替代，保留作为fallback
                            pass
                        
                        # 6. 如果从课程名称中都没有提取到教师信息，才使用font标签中的信息
                        if not teacher and 'font_teacher' in locals():
                            teacher = font_teacher
                        
                        # 7. 最后检查是否有空格分隔的教师信息
                        elif not teacher and course_name and ' ' in course_name:
                            parts = course_name.split()
                            if len(parts) > 1:
                                # 检查最后一部分是否看起来像教师姓名
                                last_part = parts[-1]
                                if len(last_part) <= 4 and not any(char.isdigit() for char in last_part):
                                    course_name = ' '.join(parts[:-1])
                                    teacher = last_part
                        
                        if course_name and course_name != '&nbsp;':
                            schedule.append({
                                "time": time_slot,
                                "day": day_name,
                                "course": course_name,
                                "weeks": weeks,
                                "classroom": classroom,
                                "teacher": teacher,
                            })

        return schedule

    def process_schedule_for_db(self, schedule):
        """
        处理解析后的课表数据，转换为符合数据库格式的数据
        """
        db_schedule = []
        
        for item in schedule:
            course = item['course']
            teacher = item['teacher'] or ''
            day_of_week = item['day']
            time = item['time']
            room = item['classroom'] or ''
            weeks = item['weeks'] or ''
            
            # 保存原始课程名称用于后续处理
            original_course = course
            
            # 无论如何都尝试从课程名称中提取周次信息，解决周次识别错误的问题
            # 这会覆盖从font标签中提取的可能错误的周次信息
            course_weeks = None
            
            # 首先尝试匹配带"(周)"后缀的周次信息，如"8(周)"或"1-7(周)"
            course_week_match = re.search(r'(\d+(?:[-\d,]+)?)\(周\)', course)
            if course_week_match:
                course_weeks = course_week_match.group(1)
            else:
                # 如果没有找到带"(周)"后缀的，尝试匹配可能的周次信息
                # 优先匹配单独的数字（如"8"）
                single_week_match = re.search(r'(?<!节)(?<!\d)\d+(?!\d)(?!节)', course)
                if single_week_match:
                    course_weeks = single_week_match.group(0)
                else:
                    # 最后尝试匹配数字范围（如"1-7"），但避免匹配节次信息
                    range_week_match = re.search(r'(?<!节)(?<!\d)\d+[-\d,]+(?!\d)(?!节)', course)
                    if range_week_match:
                        course_weeks = range_week_match.group(0)
            
            # 如果从课程名称中提取到了周次信息，优先使用它
            if course_weeks:
                weeks = course_weeks
            
            # 清理课程名称，去掉可能包含的教师信息、周次信息等
            # 去掉包含(周)的部分
            if '(周)' in course:
                course = course.split('(周)')[0]
            # 去掉包含[的部分
            if '[' in course:
                course = course.split('[')[0]
            # 去掉周次信息（范围如1-7、逗号分隔如1,3,5）
            course = re.sub(r'\d+-\d+', '', course)
            course = re.sub(r'\d+,\d+', '', course)
            course = re.sub(r'\d+\(周\)', '', course)
            course = re.sub(r'\[\d+-\d+节\]', '', course)
            
            # 如果有教师信息，尝试从课程名称中移除教师姓名
            if teacher:
                # 检查教师姓名是否在课程名称的末尾
                if course.endswith(teacher):
                    course = course[:-len(teacher)].strip()
                else:
                    # 如果不在末尾，尝试简单替换（但要小心，避免误删）
                    # 只有当教师姓名长度>=2且不是课程名的核心部分时才替换
                    if len(teacher) >= 2:
                        course = course.replace(teacher, '').strip()
            
            # 去掉可能残留的标点符号
            course = re.sub(r'[^\u4e00-\u9fa5a-zA-Z（）()]', '', course).strip()
            
            # 处理星期几格式：将"星期一"转换为"周一"，"星期二"转换为"周二"等
            # 数据库格式要求存储"周一"、"周二"到"周日"
            if day_of_week.startswith('星期'):
                day_of_week = day_of_week.replace('星期', '周')
            
            # 处理时间格式：支持多种节次格式
            # 如"1,2" -> "[1-2节]"，"[01-02-03-04节]" -> "[1-4节]"
            
            # 检查是否已经是[xx节]格式
            bracket_match = re.match(r'\[(\d+)[\-\d]+节\]', time)
            if bracket_match:
                # 提取所有数字
                nums = re.findall(r'\d+', time)
                if nums:
                    # 去掉0前缀
                    start = nums[0].lstrip('0')
                    end = nums[-1].lstrip('0')
                    time = f'[{start}-{end}节]'
            else:
                # 处理"1,2"格式
                time_match = re.match(r'(\d+),(\d+)', time)
                if time_match:
                    start, end = time_match.groups()
                    # 去掉0前缀
                    start = start.lstrip('0')
                    end = end.lstrip('0')
                    time = f'[{start}-{end}节]'
            
            # 拆分为单独的周次
            week_list = self.parse_weeks(weeks)
            
            # 为每个周次创建一条记录
            for week in week_list:
                db_schedule.append({
                    'course': course,
                    'teacher': teacher,
                    'day_of_week': day_of_week,
                    'time': time,
                    'room': room,
                    'weeks': str(week)
                })
        
        return db_schedule
    
    def parse_weeks(self, weeks_str):
        """
        解析周次字符串，返回单独的周次列表
        例如：'1-7' -> [1,2,3,4,5,6,7]
             '1,3,5,7,9,11,13,15' -> [1,3,5,7,9,11,13,15]
             '9-12(单周)' -> [9, 11]
             '10-14(双周)' -> [10, 12, 14]
             '5(周)' -> [5]
        """
        if not weeks_str:
            return []
        
        week_list = []
        # 分割多个周次段（用逗号分隔）
        week_segments = weeks_str.split(',')
        
        for segment in week_segments:
            segment = segment.strip()
            # 检查是否有单周/双周标记
            is_single_week = '(单周)' in segment
            is_double_week = '(双周)' in segment
            
            # 移除单周/双周标记和普通周标记
            segment = segment.replace('(单周)', '').replace('(双周)', '').replace('(周)', '')
                
            if '-' in segment:
                # 处理范围，如 '1-7'
                try:
                    start, end = segment.split('-')
                    start = int(start.strip())
                    end = int(end.strip())
                    
                    # 生成范围内的所有周次
                    temp_weeks = list(range(start, end + 1))
                    
                    # 过滤单周/双周
                    if is_single_week:
                        temp_weeks = [week for week in temp_weeks if week % 2 == 1]
                    elif is_double_week:
                        temp_weeks = [week for week in temp_weeks if week % 2 == 0]
                    
                    week_list.extend(temp_weeks)
                except ValueError:
                    continue
            else:
                # 处理单个周次，如 '5' 或 '5(周)'
                try:
                    # 尝试直接转换为整数
                    week_num = int(segment)
                except ValueError:
                    # 如果失败，尝试提取数字
                    num_match = re.search(r'(\d+)', segment)
                    if num_match:
                        week_num = int(num_match.group(1))
                    else:
                        continue
                
                # 应用单周/双周过滤
                if (is_single_week and week_num % 2 == 1) or (is_double_week and week_num % 2 == 0) or not (is_single_week or is_double_week):
                    week_list.append(week_num)
        
        return sorted(list(set(week_list)))  # 去重并排序

    def get_user_info(self):
        """
        获取用户信息，提取学生姓名、学生编号、所属院系、专业名称和班级名称
        """
        url = "https://jw.wfu.edu.cn/jsxsd/framework/xsMain_new.jsp?t1=1"
        response = self.ses.get(url)
        
        # 使用BeautifulSoup解析HTML
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 初始化用户信息字典
        user_info = {
            'student_name': '',
            'student_id': '',
            'department': '',
            'major': '',
            'class_name': ''
        }
        
        # 查找包含用户信息的div元素
        info_divs = soup.find_all('div', class_='middletopdwxxcont')
        
        if len(info_divs) >= 5:
            user_info['student_name'] = info_divs[1].get_text(strip=True)  # 学生姓名
            user_info['student_id'] = info_divs[2].get_text(strip=True)    # 学生编号
            user_info['department'] = info_divs[3].get_text(strip=True)    # 所属院系
            user_info['major'] = info_divs[4].get_text(strip=True)         # 专业名称
            user_info['class_name'] = info_divs[5].get_text(strip=True)    # 班级名称
        
        # 不再打印用户信息
        
        return user_info
        
    def get_all_score(self):
        """
        获取所有学期成绩，并提取符合数据库格式的字段：学期、课程名、成绩和学分
        """
        url = "https://jw.wfu.edu.cn/jsxsd/yxszzy/yxkc_list"
        response = self.ses.get(url)
        
        # 使用BeautifulSoup解析HTML
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 查找所有表格
        all_tables = soup.find_all('table')
        
        if not all_tables:
            # 不再打印未找到表格提示
            return []
            
        # 查找包含课程信息的表格（根据表头判断）
        score_table = None
        for table in all_tables:
            # 查找表头
            headers = table.find_all('th')
            if headers and len(headers) >= 5:
                # 检查是否包含关键表头
                header_texts = [th.get_text(strip=True) for th in headers]
                if '学年学期' in header_texts and '课程名称' in header_texts and '学分' in header_texts:
                    score_table = table
                    break
        
        # 如果没找到，使用第一个表格
        if not score_table:
            score_table = all_tables[0]
        
        # 初始化成绩列表
        scores = []
        
        # 遍历表格中的所有行（跳过表头）
        rows = score_table.find_all('tr')[1:]
        for row in rows:
            cells = row.find_all('td')
            if len(cells) >= 9:
                # 提取需要的字段（根据HTML结构确定索引）
                term = cells[1].get_text(strip=True)  # 学期（第2列）
                course = cells[3].get_text(strip=True)  # 课程名（第4列）
                credit = cells[5].get_text(strip=True)  # 学分（第6列）
                score = cells[7].get_text(strip=True)  # 成绩（第8列）
                
                # 构建成绩记录（符合数据库格式）
                score_record = {
                    'term': term,
                    'course': course,
                    'score': score,
                    'credit': credit
                }
                
                scores.append(score_record)
        
        # 不再打印成绩信息
        
        return scores

    # 获取用户专业排名
    def get_user_major_ranking(self):
        """
        获取用户专业排名、课程门数、不及格门数和平均学分绩点
        返回格式: {'course_count': int, 'failed_count': int, 'gpa': float, 'major_ranking': int}
        """
        url = "https://jw.wfu.edu.cn/jsxsd/yxszzy/yxszzy_grxx_ck"
        response = self.ses.get(url)
        
        # 使用BeautifulSoup解析HTML
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 初始化结果字典
        result = {
            'course_count': 0,
            'failed_count': 0,
            'gpa': 0.0,
            'major_ranking': 0
        }
        
        # 查找包含信息的表格
        table = soup.find('table', {'cellpadding': '5', 'cellspacing': '5', 'width': '100%'})
        if table:
            # 查找第一行（包含我们需要的信息）
            row = table.find('tr')
            if row:
                # 查找所有单元格
                cells = row.find_all('td')
                if len(cells) >= 5:
                    # 提取课程门数（第二个单元格）
                    course_count_cell = cells[1]
                    course_count_text = course_count_cell.get_text(strip=True)
                    course_count_match = re.search(r'课程门数：(\d+)', course_count_text)
                    if course_count_match:
                        result['course_count'] = int(course_count_match.group(1))
                    
                    # 提取不及格门数（第三个单元格）
                    failed_count_cell = cells[2]
                    failed_count_text = failed_count_cell.get_text(strip=True)
                    failed_count_match = re.search(r'不及格门数：(\d+)', failed_count_text)
                    if failed_count_match:
                        result['failed_count'] = int(failed_count_match.group(1))
                    
                    # 提取平均学分绩点（第四个单元格）
                    gpa_cell = cells[3]
                    gpa_text = gpa_cell.get_text(strip=True)
                    gpa_match = re.search(r'平均学分绩点：(\d+\.\d+)', gpa_text)
                    if gpa_match:
                        result['gpa'] = float(gpa_match.group(1))
                    
                    # 提取专业排名（第五个单元格）
                    ranking_cell = cells[4]
                    ranking_text = ranking_cell.get_text(strip=True)
                    ranking_match = re.search(r'专业排名：(\d+)', ranking_text)
                    if ranking_match:
                        result['major_ranking'] = int(ranking_match.group(1))
        
        return result

def test_special_cases():
    # 测试单周/双周解析
    print("\n=== 测试单周/双周解析 ===")
    test_weeks = [
        "9-12(单周)",  # 应解析为 [9, 11]
        "3-8(双周)",   # 应解析为 [4, 6, 8]
        "1-16",        # 应解析为 [1-16]
        "5(周)"
    ]
    wu = School()
    for week_str in test_weeks:
        result = wu.parse_weeks(week_str)
        print(f"周次字符串: {week_str} -> 解析结果: {result}")

    # 测试从课程描述中提取单双周信息
    print("\n=== 测试从课程描述中提取单双周信息 ===")
    test_course_descriptions = [
        "高等数学1-12(单周)[01-02节]2101",
        "线性代数3-10(双周)[03-04节]3102",
        "大学物理5(周)[05-06节]4103"
    ]
    for course_desc in test_course_descriptions:
        # 模拟parse_class_schedule中的处理逻辑
        # 提取周次信息
        week_match = re.search(r'(\d+(?:[-,\d]+)?)(?:\(单周\)|\(双周\)|\(周\))', course_desc)
        if week_match:
            week_str = week_match.group(1)
            # 检查是否有单双周标记
            is_single_week = '(单周)' in course_desc
            is_double_week = '(双周)' in course_desc
            
            if is_single_week:
                week_str_with_tag = f"{week_str}(单周)"
            elif is_double_week:
                week_str_with_tag = f"{week_str}(双周)"
            else:
                week_str_with_tag = f"{week_str}(周)"
            
            result = wu.parse_weeks(week_str_with_tag)
            print(f"课程描述: {course_desc} -> 提取的周次: {week_str_with_tag} -> 解析结果: {result}")

    # 测试节次解析
    print("\n=== 测试节次解析 ===")
    test_sections = [
        "[01-02-03-04节]",  # 应解析为 "1-4节"
        "[01-02-03节]",     # 应解析为 "1-3节"
        "[01-02节]"         # 应解析为 "1-2节"
    ]
    for section_str in test_sections:
        # 提取节次数字
        numbers = re.findall(r'\d+', section_str)
        if len(numbers) >= 2:
            start = int(numbers[0])
            end = int(numbers[-1])
            formatted = f"{start}-{end}节"
        else:
            formatted = section_str
        print(f"节次字符串: {section_str} -> 格式化结果: {formatted}")


def main():
    # 登录系统
    a = School()
    # login_success = a.login_in("22111101419", "123456cyj")
    login_success = a.login_in("24151201421", "20040928Mmj")
    
    if not login_success:
        print("❌ 登录失败，程序退出！")
        return

    # 获取专业排名
    # ranking_info = a.get_user_major_ranking()
    # print(ranking_info)

    # 获取课表信息
    html = a.get_class_schedule("2024-2025-2")  # 2024-2025 第二学期
    # print(f"获取到的HTML内容: {html}")
    schedule = a.parse_class_schedule(html)
    print(f"原始解析结果:{schedule}")
    if schedule:
        # 不再打印原始解析结果和数据库格式数据
        db_schedule = a.process_schedule_for_db(schedule)
        print(db_schedule)
    else:
        print("❌ 解析课表失败！")
        # 不再打印解析课表失败提示

    # # 运行特殊情况测试
    # # test_special_cases()

    # # 获取用户信息
    # user_info = a.get_user_info()
    # print(user_info)
    # 获取所有学期成绩
    # a.get_all_score()


if __name__ == '__main__':
    main()